﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace IFT.Classes
{
  #region UnitEnums
  public enum UnitType
  {
    MinUnitFieldType,
    HeavyInfantry,
    LightInfantry,
    LongRange,
    Artillery,
    AirDefense,
    Bomber,
    Support,
    MaxUnitFieldType
  };
  #endregion UnitEnums
  class UnitDamage
  {
    private int m_hitpoints;
    private int m_damagerecieved;
    private int m_damagedone;
    private int m_healrecieved;
    private int m_healdone;

    public int Hitpoints
    {
      get { return m_hitpoints; }
      set { m_hitpoints = (int)value; }
    }

    public int DamageRecieved
    {
      get { return m_damagerecieved; }
      set { m_damagerecieved = (int)value; }
    }

    public int DamageDone
    {
      get { return m_damagedone; }
      set { m_damagedone = (int)value; }
    }

    public int HealRecieved
    {
      get { return m_healrecieved; }
      set { m_healrecieved = (int)value; }
    }

    public int HealDone
    {
      get { return m_healdone; }
      set { m_healdone = (int)value; }
    }

    public UnitDamage() { }
    public UnitDamage(int HP) { m_hitpoints = HP; }
  }

  class Unit
  {
    private XMLUnit m_unit            = null;
    private Player m_owner            = null;
    private UnitType m_type           = UnitType.MinUnitFieldType;
    private List<UnitDamage> m_damage = new List<UnitDamage>();

    #region UnitPropertys
    public XMLUnit Info
    {
      get { return m_unit; }
    }

    public UnitType Type
    {
      get { return m_type; }
      set { m_type = (UnitType)value; }
    }

    public Player Owner
    {
      get { return m_owner; }
    }

    public int Count
    {
      get { return m_damage.Count; }
      set
      {
        if (IsLoaded())
        {
          m_damage.Clear();

          if ((int)value > 0)
          {
            for (int i = 0; i < (int)value; i++)
              m_damage.Add(new UnitDamage(Info.Stats.Hitpoints));
          }
        }
      }
    }

    public UnitDamage this[int index]
    {
      get
      {
        if (index < 0 || index >= Count)
          return null;

        return m_damage[index];
      }
      set { m_damage[index] = (UnitDamage)value; }
    }
    #endregion UnitPropertys
    #region UnitOperators
    public static bool operator ==(Unit x, Unit y)
    {
      if (!x.IsLoaded() || !y.IsLoaded())
        return false;

      return x.Info.ID == y.Info.ID;
    }

    public static bool operator !=(Unit x, Unit y)
    {
      if (!x.IsLoaded() || !y.IsLoaded())
        return false;

      return x.Info.ID == y.Info.ID;
    }

    public override int GetHashCode()
    {
      return Info.ID ^ m_damage.GetHashCode() ^ (int)Owner.Role;
    }

    public bool Equals(Unit Obj)
    {
      if (!IsLoaded())
        return false;

      return m_unit.ID == Obj.Info.ID;
    }

    public bool Equals(XMLUnit Obj)
    {
      if (!IsLoaded())
        return false;

      return m_unit.ID == Obj.ID;
    }

    public override bool Equals(object Obj)
    {
      if (Obj == null)
        return false;

      if (Obj is Unit)
        return Equals(Obj as Unit);

      if (Obj is XMLUnit)
        return Equals(Obj as XMLUnit);

      return (Obj.GetType() == GetType());
    }
    #endregion UnitOperators

    public Unit() { }
    public Unit(Player owner) { m_owner = owner; }

    public bool IsLoaded() { return (Info != null); }
    public bool IsType(UnitType UType)
    {
      if (!IsLoaded())
        return false;

      return UType == Info.Type;
    }
    
    public bool IsAlive(int UnitId) { return (this[UnitId].Hitpoints > 0) ? true : false; }
    public bool IsAlive()
    {
      foreach (UnitDamage damage in m_damage)
      {
        if (damage.Hitpoints > 0)
          return true;
      }
      return false;
    }

    public void LoadUnit(Unit unit) { if (unit != null) LoadUnit(unit.Info.ID); }
    public void LoadUnit(int UnitId) { LoadUnit(UnitManager.Instance.GetUnit(UnitId)); }
    public void LoadUnit(XMLUnit unit) { m_unit = unit; }

    public void PushToField()
    {
      if (!IsLoaded())
        return;

      int avaibleUnits  = Owner.GetReserveCount(Info.ID);
      int maxUnit       = GetFieldLimit();
      int unitsToPush   = maxUnit - (maxUnit - avaibleUnits);

      if (Count == maxUnit)
      {
        foreach (UnitDamage damage in m_damage)
        {
          if (damage.Hitpoints <= 0 && avaibleUnits <= 0)
          {
            damage.Hitpoints = m_unit.Stats.Hitpoints;
            avaibleUnits--;
          }
        }
      }
      else
      {
        Count = Count + unitsToPush;
        avaibleUnits -= unitsToPush;
      }

      Owner.SetReserveCount(m_unit.ID, avaibleUnits);
    }

    public void PushToReserve()
    {
      if (!IsLoaded())
        return;

      int unitIncrease = 0;

      if (Count > 0)
      {
        foreach (UnitDamage unit in m_damage)
        {
          if (unit.Hitpoints >= 0)
            unitIncrease++;
        }
      }

      Owner.SetReserveCount(m_unit.ID, unitIncrease);
    }

    public int GetBuildTime(int barrackLevel)
    {
      if (!IsLoaded() || barrackLevel < Info.Stats.BarrackLevel)
        return 0;

      return (int)Math.Round((double)Info.Stats.BuildTime * Math.Pow(0.95, (double)(barrackLevel - Info.Stats.BarrackLevel)));
    }

    public void GetLog(ref UnitDamage Log)
    {
      foreach (UnitDamage damage in m_damage)
      {
        Log.Hitpoints += damage.Hitpoints;
        Log.DamageDone += damage.DamageDone;
        Log.DamageRecieved += damage.DamageRecieved;
        Log.HealDone += damage.HealDone;
        Log.HealRecieved += damage.HealRecieved;
      }
    }

    public void DealDamage()
    {
    }

    public void RecieveDamage(Unit Attacker)
    {
      if (!IsLoaded())
        return;

      int AttackCount = 0;
      int totalDamage = Attacker.Info.Stats.Damage - Info.Stats.Armour;
      int maxUnits = (Attacker.Count < Count) ? Count : Attacker.Count;

      while (AttackCount < Attacker.Count)
      {
        if (Attacker.IsAlive(AttackCount))
        {
          for (int x = 0; x < Count; x++)
          {
            if (this[x].Hitpoints > 0)
            {
              if (totalDamage >= this[x].Hitpoints)
                this[x].DamageRecieved = this[x].Hitpoints;
              else
                this[x].DamageRecieved += totalDamage;
              break;
            }
          }
        }
        AttackCount++;
      }
    }

    public void EndRound()
    {
      if (Count > 0)
      {
        foreach (UnitDamage unit in m_damage)
        {
          unit.Hitpoints -= unit.DamageRecieved;
        }
      }
    }

    public int GetFieldLimit()
    {
      if (!IsLoaded())
        return 0;

      int slotSize = 30;

      if (Owner.BattleField.Type != BattlefieldType.Sea)
      {
        if (Info.Type == UnitType.Bomber || Info.Type == UnitType.AirDefense)
          slotSize = 10 * (int)m_owner.BattleField.Type;
      }
      else
      {
        if (Info.Type == UnitType.LongRange || Info.Type == UnitType.HeavyInfantry)
          slotSize = 10;
      }

      slotSize -= slotSize % Info.Stats.Size;
      return slotSize;
    }
  }
}
